--- 模块功能：lcddemo
-- @module lcd
-- @author Dozingfiretruck
-- @release 2021.01.25

-- LuaTools需要PROJECT和VERSION这两个信息
PROJECT = "weather"
VERSION = "1.0.2"

log.info("main", PROJECT, VERSION)

-- sys库是标配
--_G.sys = require("sys")
local sys = require("sys")
local sysplus = require("sysplus")
--[[本demo需要lbsLoc库与libnet库, 库位于script\libs, 需require]]
local lbsLoc = require("lbsLoc")
local libnet = require("libnet")
local libfota = require("libfota")
--require "SHT20"
local g_senser = require "SC7A20"

--[[注意：此处的PRODUCT_KEY仅供演示使用，不保证一直能用，量产项目中一定要使用自己在iot.openluat.com中创建的项目productKey]]
PRODUCT_KEY = "JWGHvNevbUFWvUmKvoggKNsEntQINWYG" --"D39w6FTeyFO1Df6uOmX3VSB8TyBJ0T74"

local lbs_result = 1
local lbs_location = ""

-- UI带屏的项目一般不需要低功耗了吧, Air101/Air103设置到最高性能
if mcu and (rtos.bsp() == "AIR101" or rtos.bsp() == "AIR103") then
    mcu.setClk(240)
end

--[[
-- LCD接法示例
LCD管脚       Air780E管脚    Air101/Air103管脚   Air105管脚         
GND          GND            GND                 GND                 
VCC          3.3V           3.3V                3.3V                
SCL          (GPIO11)       (PB02/SPI0_SCK)     (PC15/HSPI_SCK)     
SDA          (GPIO09)       (PB05/SPI0_MOSI)    (PC13/HSPI_MOSI)    
RES          (GPIO01)       (PB03/GPIO19)       (PC12/HSPI_MISO)    
DC           (GPIO10)       (PB01/GPIO17)       (PE08)              
CS           (GPIO08)       (PB04/GPIO20)       (PC14/HSPI_CS)      
BL(可以不接)  (GPIO22)       (PB00/GPIO16)       (PE09)              

提示:
1. 只使用SPI的时钟线(SCK)和数据输出线(MOSI), 其他均为GPIO脚
2. 数据输入(MISO)和片选(CS), 虽然是SPI, 但已复用为GPIO, 并非固定,是可以自由修改成其他脚
3. 若使用多个SPI设备, 那么RES/CS请选用非SPI功能脚
4. BL可以不接的, 若使用Air10x屏幕扩展板,对准排针插上即可
]]


--PWM蜂鸣器  GPIO24 PWM01
local PWM_ID = 1
if rtos.bsp() == "EC618" then
    PWM_ID = 1 -- GPIO24
elseif rtos.bsp() == "AIR101" or rtos.bsp() == "AIR103" then
    PWM_ID = 4 -- GPIO 4
elseif rtos.bsp():startsWith("ESP32") then
    -- 注意, ESP32系列的PWM, PWM通道均与GPIO号相同
    -- 例如需要用GPIO1输出PWM, 对应的PWM通道就是1
    -- 需要用GPIO16输出PWM, 对应的PWM通道就是16
    if rtos.bsp() == "ESP32C3" then
        PWM_ID = 12 -- GPIO 12
    elseif rtos.bsp() == "ESP32S3" then
        PWM_ID = 11 -- GPIO 11
    end
elseif rtos.bsp() == "AIR105" then
    PWM_ID = 1 -- GPIO 17
end

--按键检测
local button_timer_outtime = 10 --按键定时器: 10ms
local button_shake_time = 1     --按键消抖时间: button_shake_time*button_timer_outtime
local button_long_time = 100    --按键长按时间: button_shake_time*button_timer_outtime

local button_detect = true
local button_state = false
local button_cont = 0

local BTN_PIN = 22 --22 -- GPIO22 MAIN_DTR
local BTN_LEFT = 11 -- GPIO11 UART2_TXD

--[[ 若固件支持防抖, 启用防抖
if gpio.debounce then
    gpio.debounce(BTN_PIN, 5)
end
]]

gpio.setup(BTN_LEFT, nil, gpio.PULLUP)
gpio.setup(BTN_PIN, nil, gpio.PULLUP)
--gpio.setup(BTN_PIN, 0)
--gpio.set(BTN_PIN, gpio.LOW)

--[[
gpio.setup(BTN_PIN, 
    function() 
        log.info("key", gpio.get(BTN_PIN))
        if not button_detect then 
            return 
        end
        button_detect = false
        button_state = true
    end, 
    gpio.PULLUP,
    gpio.FALLING)

button_timer = sys.timerLoopStart(function()
    if button_state then
        if button() == 0 then
            button_cont = button_cont + 1
            if button_cont > button_long_time then
                print("long pass")
            end
        else 
            if button_cont < button_shake_time then
            else
                if button_cont < button_long_time then
                    print("pass")
                else
                    print("long pass")
                end
            end
            button_cont = 0
            button_state = false
            button_detect = true
        end
    end
end,button_timer_outtime) 
]]
--添加硬狗防止程序卡死
if wdt then
    wdt.init(9000)--初始化watchdog设置为9s
    sys.timerLoopStart(wdt.feed, 3000)--3s喂一次狗
end

local rtos_bsp = rtos.bsp()

-- 启动时对rtc进行判断和初始化
local t = rtc.get()
log.info("rtc", json.encode(t))
if t["year"] == 1900 then
    -- 对air101/air103来说
    -- 若复位启动, 那rtc时间必然是1900年, 设置时间就行
    -- 若为唤醒, 那rtc时间必然已经设置过, 肯定不是1900, 就不用设置了
    rtc.set({year=2022,mon=9,day=1,hour=0,min=0,sec=0})
end

-- 根据不同的BSP返回不同的值
-- spi_id,pin_reset,pin_dc,pin_cs,bl
function lcd_pin()
    if rtos_bsp == "AIR101" then
        return 0,pin.PB03,pin.PB01,pin.PB04,pin.PB00
    elseif rtos_bsp == "AIR103" then
        return 0,pin.PB03,pin.PB01,pin.PB04,pin.PB00
    elseif rtos_bsp == "AIR105" then
        return 5,pin.PC12,pin.PE08,pin.PC14,pin.PE09
    elseif rtos_bsp == "ESP32C3" then
        return 2,10,6,7,11
    elseif rtos_bsp == "ESP32S3" then
        return 2,16,15,14,13
    elseif rtos_bsp == "EC618" then
        return 0,1,10,8,22
    else
        log.info("main", "bsp not support")
        return
    end
end

local spi_id,pin_reset,pin_dc,pin_cs,bl = lcd_pin() 

-- v0006及以后版本可用pin方式, 请升级到最新固件 https://gitee.com/openLuat/LuatOS/releases
spi_lcd = spi.deviceSetup(spi_id,pin_cs,0,0,8,20*1000*1000,spi.MSB,1,0)

--[[ 此为合宙售卖的2.4寸TFT LCD 分辨率:240X320 屏幕ic:GC9306 购买地址:https://item.taobao.com/item.htm?spm=a1z10.5-c.w4002-24045920841.39.6c2275a1Pa8F9o&id=655959696358]]
-- lcd.init("gc9a01",{port = "device",pin_dc = pin_dc, pin_pwr = bl, pin_rst = pin_reset,direction = 0,w = 240,h = 320,xoffset = 0,yoffset = 0},spi_lcd)

--[[ 此为合宙售卖的1.8寸TFT LCD LCD 分辨率:128X160 屏幕ic:st7735 购买地址:https://item.taobao.com/item.htm?spm=a1z10.5-c.w4002-24045920841.19.6c2275a1Pa8F9o&id=560176729178]]
--lcd.init("st7735",{port = "device",pin_dc = pin_dc, pin_pwr = bl, pin_rst = pin_reset,direction = 0,w = 128,h = 160,xoffset = 0,yoffset = 0},spi_lcd)

--[[ 此为合宙售卖的1.54寸TFT LCD LCD 分辨率:240X240 屏幕ic:st7789 购买地址:https://item.taobao.com/item.htm?spm=a1z10.5-c.w4002-24045920841.20.391445d5Ql4uJl&id=659456700222]]
lcd.init("st7789",{port = "device",pin_dc = pin_dc, pin_pwr = bl, pin_rst = pin_reset,direction = 0,w = 240,h = 240,xoffset = 0,yoffset = 0},spi_lcd)

--[[ 此为合宙售卖的0.96寸TFT LCD LCD 分辨率:160X80 屏幕ic:st7735s 购买地址:https://item.taobao.com/item.htm?id=661054472686]]
--lcd.init("st7735v",{port = "device",pin_dc = pin_dc, pin_pwr = bl, pin_rst = pin_reset,direction = 1,w = 160,h = 80,xoffset = 0,yoffset = 24},spi_lcd)
--如果显示颜色相反，请解开下面一行的注释，关闭反色
--lcd.invoff()
--如果显示依旧不正常，可以尝试老版本的板子的驱动
--lcd.init("st7735s",{port = "device",pin_dc = pin_dc, pin_pwr = bl, pin_rst = pin_reset,direction = 2,w = 160,h = 80,xoffset = 0,yoffset = 0},spi_lcd)

--[[ 此为合宙售卖的2.4寸TFT LCD 分辨率:240X320 屏幕ic:GC9306 购买地址:https://item.taobao.com/item.htm?spm=a1z10.5-c.w4002-24045920841.39.6c2275a1Pa8F9o&id=655959696358]]
-- lcd.init("gc9306",{port = "device",pin_dc = pin_dc , pin_pwr = bl,pin_rst = pin_reset,direction = 0,w = 240,h = 320,xoffset = 0,yoffset = 0},spi_lcd)

-- 不在上述内置驱动的, 看demo/lcd_custom
lcd.setFont(lcd.font_opposansm10_chinese) -- 具体取值可参考api文档的常量表


-- 订阅式, 模块本身会周期性查询基站信息,但通常不包含临近小区
sys.subscribe("CELL_INFO_UPDATE", function()
    log.info("cell", json.encode(mobile.getCellInfo()))
end)

-- 获取sim卡的状态
sys.subscribe("SIM_IND", function(status)
    log.info("sim status", status)
    if status == 'GET_NUMBER' then
        log.info("number", mobile.number(0))
    end
end)


local color_back = 0x0
local color_front = 0xFFFF
function Show_Num(x,y,num,Hsize,color)
    if Hsize < 40 then
        strFile = "/luadb/s"..num..".bin"
        width = 16
        height = 32
    elseif Hsize>=80 then
        strFile = "/luadb/b"..num..".bin"
        width = 48
        height = 80
    else   
        strFile = "/luadb/m"..num..".bin"
        width = 20
        height = 40
    end

    if color ~= nil then lcd.setColor(color_back, color)  end
    
    data = io.readFile(strFile)
    if data ~= nil  then 
        lcd.drawXbm(x, y, width, height, data)  
    else
        lcd.fill(x,y,x+width,y+height,color_back)
    end

    if color ~= nil then lcd.setColor(color_back, color_front)  end
end

function Show_Date(x,y,month,day,week)
    local strFile1 = "/luadb/yue.bin"
    local strFile2 = "/luadb/ri.bin"
    local strFile3 = "/luadb/zhou.bin"
    
    local data1 = io.readFile(strFile1)
    local data2 = io.readFile(strFile2)
    local data3 = io.readFile(strFile3)

    --月数字显示
    Show_Num(x, y, string.format("%d", month//10), 32);
    x = x+16
    Show_Num(x, y, string.format("%d", month%10), 32);    
    x = x+16 
    --月
    if data1 ~= nil  then 
        lcd.drawXbm(x, y, 32, 32, data1)  
    else
        lcd.fill(x,y,x+32,y+32,color_back)
    end
    x = x+32

    --日数字显示
    Show_Num(x, y, string.format("%d", day//10), 32);
    x = x+16
    Show_Num(x, y, string.format("%d", day%10), 32);
    x = x+16
    --日
    if data2 ~= nil  then 
        lcd.drawXbm(x, y, 32, 32, data2)  
    else
        lcd.fill(x,y,x+32,y+32,color_back)
    end 
    x = x + 32

    --周   
    x = x + 10
    if data3 ~= nil  then 
        lcd.drawXbm(x, y, 32, 32, data3)  
    else
        lcd.fill(x,y,x+32,y+32,color_back)
    end
    x = x + 32
    week=week-1
    local strWeekFile = "/luadb/c"..week..".bin"
    local dataW = io.readFile(strWeekFile)
    if dataW ~= nil  then 
        if week == 0 or week == 6 then
            lcd.setColor(color_back, 0x07e0) --红色
            lcd.drawXbm(x, y, 32, 32, dataW)  
            lcd.setColor(color_back, color_front)
        else
            lcd.setColor(color_back, 0x001F) --绿色
            lcd.drawXbm(x, y, 32, 32, dataW)  
            lcd.setColor(color_back, color_front)
        end
        
    else
        lcd.fill(x,y,x+32,y+32,color_back)
    end
end

local weather_result = 0
local weather=""
local city=""
local temperature=""
local humidity=""
local winddirection=""
local windpower=""
local reflesh = 0

--页面0显示
function show_page0(show)
 --获取时间格式table
 local date = os.date("*t")
 --log.info("rtc", json.encode(date))
 --log.info("rtc", date["year"],date["month"],date["day"],date["hour"],date["min"],date["sec"]);
 local strTime = string.format("%04d/%02d/%02d %02d:%02d:%02d",date["year"],date["month"],date["day"],date["hour"],date["min"],date["sec"])
 
 --lcd.fill(20, 0, 240, 20,color_back)
 --lcd.drawStr(20,20,strTime)

 --时间戳获取
 local time = os.time()
 --log.info("rtc", time);

 local jsoncode

 --时间值绘制
 local time_x=10
 local time_y=96
 Show_Num(time_x, time_y, string.format("%d", date["hour"]//10), 80, 0xFDB8);
 time_x = time_x + 40
 Show_Num(time_x, time_y, string.format("%d", date["hour"]%10), 80, 0xFDB8);
 time_x = time_x + 40
 Show_Num(time_x, time_y, string.format("%d", date["min"]//10), 80, 0xFA68);
 time_x = time_x + 40
 Show_Num(time_x, time_y, string.format("%d", date["min"]%10), 80, 0xFA68);
 time_x = time_x + 40
 --秒显示
 time_x = time_x + 10
 time_y = time_y + 40
 Show_Num(time_x, time_y, string.format("%d", date["sec"]//10), 40);
 time_x = time_x + 20
 Show_Num(time_x, time_y, string.format("%d", date["sec"]%10), 40);
 time_x = time_x + 20
 time_y = time_y + 40
 --月 日 星期 显示
 time_y = time_y + 10
 time_x = 0
 Show_Date(time_x, time_y,date["month"],date["day"],date["wday"])

 if weather_result == 1 and show ==  1 then
     --weather_result = 0
     if string.find(weather,"大雨") ~= nil then
         lcd.showImage(110,0,"/luadb/rainH.jpg")
     elseif string.find(weather,"雨") ~= nil then
         log.info("rainS.jpg")
         lcd.showImage(110,0,"/luadb/rainS.jpg")
     elseif string.find(weather,"晴") ~= nil then
         log.info("sunny.jpg")
         lcd.showImage(110,0,"/luadb/sunny.jpg")
     elseif string.find(weather,"阴") ~= nil then
         log.info("cloudH.jpg")
         lcd.showImage(110,0,"/luadb/cloudH.jpg")
     elseif string.find(weather,"云") ~= nil then
         log.info("cloudS.jpg")
         lcd.showImage(110,0,"/luadb/cloudS.jpg")
     elseif string.find(weather,"雪") ~= nil then
         log.info("snow.jpg")
         lcd.showImage(110,0,"/luadb/snow.jpg")
     elseif string.find(weather,"雾") ~= nil then
         log.info("fog.jpg")
         lcd.showImage(110,0,"/luadb/fog.jpg")
     else
         log.info("not find weather.jpg")
     end
     lcd.showImage(0,10,"/luadb/temp.jpg")
     lcd.showImage(0,45,"/luadb/humi.jpg")
     lcd.fill(40, 0, 110, 93,color_back)
     lcd.drawStr(40,15,"当前温湿度:")
     lcd.drawStr(40,35," "..temperature)
     lcd.drawXbm(40+25,25,12,12, string.char(
         0x02,0x00,0xE5,0x02,0x12,0x03,0x08,0x02,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x04,0x10,0x02,0xE0,0x01,0x00,0x00,0x00,0x00))
     lcd.drawStr(40,65,humidity.." %")

     lcd.fill(0, 225, 240, 240,color_back)
     lcd.drawStr(0,235, city)

     if lbs_location ~= "" then
         lcd.drawStr(80,235, "V"..VERSION.." L:"..lbs_location)
     end
 end
end
--页面1显示
function show_page1(show)
    lcd.drawStr(80,20,"未来天气")
    if jsoncode.forecasts == nil then 
        log.warn("forecasts=nil")
    else
        local y = 40        
        for i = 1 , 4, 1 do
            if type(jsoncode.forecasts[i]) == "table" then
                lcd.drawStr(10,y,jsoncode.forecasts[i].date)
                lcd.drawStr(120,y,"白天:"..jsoncode.forecasts[i].dayweather..",夜晚:"..jsoncode.forecasts[i].nightweather)
                lcd.drawStr(10,y+20,"温度:"..jsoncode.forecasts[i].nighttemp_float.."-"..jsoncode.forecasts[i].daytemp_float)
                lcd.drawStr(120,y+20,"风力:"..jsoncode.forecasts[i].daywind.." "..jsoncode.forecasts[i].daypower.."级")           
            end
            y = y + 50
        end
    end
end

--显示任务
sys.taskInit(function()

    pwm.open(PWM_ID, 1000, 50)
    sys.wait(200)
    pwm.close(PWM_ID)

    lcd.clear()
    log.info("wiki", "https://wiki.luatos.com/api/lcd.html")
    if lcd.showImage then
        -- 注意, jpg需要是常规格式, 不能是渐进式JPG
        -- 如果无法解码, 可以用画图工具另存为,新文件就能解码了
        lcd.showImage(0,0,"/luadb/logo.jpg")
        sys.wait(3000)
    end
    lcd.clear(color_back)  --黑色背景    
    lcd.setColor(color_back, color_front)  --背景黑色，前景白色
    rtc.timezone(32) --东8区
    reflesh=1
    pre_page = 0
    page = 0
    while 1 do 
        
        --log.info("key1",gpio.get(BTN_PIN))
        --log.info("key2",gpio.get(BTN_LEFT))
        page = g_senser.getPage()
        if pre_page ~=  page then
            log.info("page change", page)
            lcd.clear(color_back)  --黑色背景
            reflesh=1
        end
        pre_page = page
        if page == 0 then
            show_page0(reflesh)
            reflesh=0
        else
            show_page1(reflesh)
            reflesh=0
        end

        --log.info("lcd.drawLine", lcd.drawLine(20,20,150,20,0x001F))
        --log.info("lcd.drawRectangle", lcd.drawRectangle(20,40,120,70,0xF800))
        --log.info("lcd.drawCircle", lcd.drawCircle(50,50,20,0x0CE0))
        sys.wait(1000)
    end
end)

-- 功能:获取基站对应的经纬度后的回调函数
-- 参数:-- result：number类型，0表示成功，1表示网络环境尚未就绪，2表示连接服务器失败，3表示发送数据失败，4表示接收服务器应答超时，5表示服务器返回查询失败；为0时，后面的5个参数才有意义
		-- lat：string类型，纬度，整数部分3位，小数部分7位，例如031.2425864
		-- lng：string类型，经度，整数部分3位，小数部分7位，例如121.4736522
        -- addr：目前无意义
        -- time：string类型或者nil，服务器返回的时间，6个字节，年月日时分秒，需要转为十六进制读取
            -- 第一个字节：年减去2000，例如2017年，则为0x11
            -- 第二个字节：月，例如7月则为0x07，12月则为0x0C
            -- 第三个字节：日，例如11日则为0x0B
            -- 第四个字节：时，例如18时则为0x12
            -- 第五个字节：分，例如59分则为0x3B
            -- 第六个字节：秒，例如48秒则为0x30
        -- locType：numble类型或者nil，定位类型，0表示基站定位成功，255表示WIFI定位成功
local function getLocCb(result, lat, lng, addr, time, locType)
    log.info("testLbsLoc.getLocCb", result, lat, lng)
    -- 获取经纬度成功
    if result == 0 then
        lbs_result = 0
        lbs_location = string.format("%.4f,%.4f", lng,lat)
        log.info("服务器返回的时间", time:toHex())
        log.info("定位类型,基站定位成功返回0", locType)
    end
    -- 广播给其他需要定位数据的task
    sys.publish("lbsloc_result", result, lat, lng)
end

function WeatherReq(Location)
    local url =""
    if Location == "" then
      url = "http://maxtrix.com.cn/weather"
    else
      url = "http://maxtrix.com.cn/weather?location="..Location
    end
    log.info("url=", url)
    local code, headers, body = http.request("GET", url).wait()
    log.info("code=", code)
    if code == 200 and body then
        --log.info("body=", body)
        jsoncode = json.decode(body)
        if jsoncode.status == "1" then
            weather_result=1
            reflesh=1
            log.info(jsoncode.weather, jsoncode.temperature,jsoncode.humidity,jsoncode.winddirection,jsoncode.windpower,jsoncode.city)
            weather=jsoncode.weather
            city=jsoncode.city
            temperature=jsoncode.temperature
            humidity=jsoncode.humidity
            winddirection=jsoncode.winddirection
            windpower=jsoncode.windpower
                
            if jsoncode.forecasts == nil then 
                log.warn("forecasts=nil")
            else
                if type(jsoncode.forecasts[1]) == "table" then
                    log.info("forecasts", jsoncode.forecasts[1].date, jsoncode.forecasts[1].dayweather, 
                    jsoncode.forecasts[1].daytemp, jsoncode.forecasts[1].nighttemp)
                end
                if type(jsoncode.forecasts[2]) == "table" then
                    log.info("forecasts", jsoncode.forecasts[2].date, jsoncode.forecasts[2].dayweather, 
                    jsoncode.forecasts[2].daytemp, jsoncode.forecasts[2].nighttemp)
                end
                if type(jsoncode.forecasts[3]) == "table" then
                    log.info("forecasts", jsoncode.forecasts[3].date, jsoncode.forecasts[3].dayweather, 
                    jsoncode.forecasts[3].daytemp, jsoncode.forecasts[3].nighttemp)
                end
                if type(jsoncode.forecasts[4]) == "table" then
                    log.info("forecasts", jsoncode.forecasts[4].date, jsoncode.forecasts[4].dayweather, 
                    jsoncode.forecasts[4].daytemp, jsoncode.forecasts[4].nighttemp)
                end
            end
        end
    end    
end

function fota_cb(ret)
    log.info("fota", ret)
    if ret == 0 then
        rtos.reboot()
    end
end
--sys.timerLoopStart(libfota.request, 3600000, fota_cb)

--位置\天气接口\OTA
sys.taskInit(function()
    sys.waitUntil("IP_READY", 30000)

    -- 使用合宙iot平台进行升级
    --libfota.request(fota_cb)
    -- 使用自建服务器进行升级
    -- local ota_url = "http://myserv.com/myapi/version=" .. _G.VERSION .. "&imei=" .. mobile.imei()
    -- libfota.request(fota_cb, ota_url)
    -- sys.timerLoopStart(libfota.request, 3600000, fota_cb, ota_url)

    --log.info("字符串查找测试1",string.find("测试字符串 大雪测试","雪"))
    --log.info("字符串查找测试2",string.find("测试字符串 大雪测试","雨"))

    while 1 do 
        lbs_result=1
        while lbs_result == 1 do
            lbsLoc.request(getLocCb)        
            sys.waitUntil("lbsloc_result", 30000)
            log.info("wait lbsloc_result")
        end
        log.info("lbs_result=", lbs_result)
        if lbs_result == 0 then
            WeatherReq(lbs_location)
        end

        sys.wait(10*1000)
        libfota.request(fota_cb)
        
        sys.wait(3600*1000) --每小时更新一次  
    end
end)

-- 用户代码已结束---------------------------------------------
-- 结尾总是这一句
sys.run()
-- sys.run()之后后面不要加任何语句!!!!!
